#pragma once

//caculated in signal.xlsx
#define EXCITE_THRESHOLD 23350

#define SIGNAL_MAX 1200
#define SIGNAL_MIN 0
#define SIGNAL_INIT 0

#define SIGNAL_BUF_SIZE 30

#define FIRE_SIG_COUNT 30
#define NORESPONSE_COUNT 5
#define RESTRAIN_COUNT 5
#define EASY_COUNT 5

typedef enum{
	NERVE_TYPE_LEFT_TO_RIGHT,
	NERVE_TYPE_RIGHT_TO_LEFT,
}NERVE_ARROW_TYPE;

typedef enum{
	NERVE_TYPE_INSIDE,
	NERVE_TYPE_AT_TOP_BOUND,
	NERVE_TYPE_AT_BOTTOM_BOUND,
}NERVE_POS_TYPE;

typedef enum{
	STATUS_CALM,
	STATUS_FIRE,
	STATUS_NORESPONSE,
	STATUS_RESTRAIN,
	STATUS_EASY,
}NERVE_STATUS;

class CNerveArray;

class CNerve
{
public:
	CNerve( NERVE_ARROW_TYPE a_tp, NERVE_POS_TYPE pos_tp, int pos_x, int pos_y ) :
	  	m_nLeftInputSignal(SIGNAL_INIT),
		m_nRightInputSignal(SIGNAL_INIT),
		m_nLeftOutputSignal(SIGNAL_INIT),
		m_nRightOutputSignal(SIGNAL_INIT),
		m_nMainInputSignal(SIGNAL_INIT),
		m_nMainOutputSignal(SIGNAL_INIT),
		m_nLeftInputSignal_Buf(SIGNAL_INIT),
		m_nRightInputSignal_Buf(SIGNAL_INIT),
		m_nMainInputSignal_Buf(SIGNAL_INIT),
		m_bLeftSignalIsMinus(false),
		m_bRightSignalIsMinus(false),
		m_pLeftInputNerve(NULL),
		m_pRightInputNerve(NULL),
		m_pLeftOutputNerve(NULL),
		m_pRightOutputNerve(NULL),
		m_pMainInputNerve(NULL),
		m_pMainOutputNerve(NULL),
		m_arrow_type(a_tp),
		m_pos_type(pos_tp),
		m_pos_x(pos_x),
		m_pos_y(pos_y),
		m_status(STATUS_CALM),
		m_countdown(0)
		{
			for( int i=0; i<SIGNAL_BUF_SIZE; i++ )
				m_sig_buf[i] = 0;
		};

	virtual ~CNerve(void) {};

	int Save( unsigned char* pNerveMem );
	bool Load( unsigned char* pNerveMem, CNerveArray* pArray );

	int GetMemSize( void ) 
	{ 
		unsigned char tmp[200];
		return Save(tmp); 
	};
	
	void LeftInput( int nSignal ) {m_nLeftInputSignal_Buf = nSignal;};
	void RightInput( int nSignal ) {m_nRightInputSignal_Buf = nSignal;};
	void Input( int nSignal ) {m_nMainInputSignal_Buf = nSignal;};

	void ReadSignal( int& nLeftIn, int& nRightIn, int& nLeftOut, int& nRightOut ) {nLeftIn=m_nLeftInputSignal; nRightIn=m_nRightInputSignal; nLeftOut=m_nLeftOutputSignal; nRightOut = m_nRightOutputSignal; };

	int GetOutput( void ) { return m_nMainOutputSignal;};

	virtual bool PrepareProcess( void );
	virtual bool ProcessSignal( void );

	//virtual void Inspire( void );
	//virtual void Punish( void );

	void GetPosition( int& x, int& y ) {x=m_pos_x; y=m_pos_y;};

	bool ConnectTo( CNerve* pNerve )
	{
		if( pNerve->ConnectIn(this) )
			m_pMainOutputNerve=pNerve;
		else
			return false;
		return true;
	};

	bool LO_ConnectToLeft( CNerve* pNerve ) 
	{
		if( pNerve->LeftConnectIn(this) )
			m_pLeftOutputNerve=pNerve;
		else
			return false;
		m_bLO_to_LI = true;
		return true;
	};
	bool LO_ConnectToRight( CNerve* pNerve ) 
	{
		if( pNerve->RightConnectIn(this) )
			m_pLeftOutputNerve=pNerve;
		else
			return false;
		m_bLO_to_LI = false;
		return true;
	};
	bool RO_ConnectToLeft( CNerve* pNerve ) 
	{
		if( pNerve->LeftConnectIn(this) )
			m_pRightOutputNerve=pNerve;
		else
			return false;
		m_bRO_to_LI = true;
		return true;
	};
	bool RO_ConnectToRight( CNerve* pNerve ) 
	{
		if( pNerve->RightConnectIn(this) )
			m_pRightOutputNerve=pNerve;
		else
			return false;
		m_bRO_to_LI = false;
		return true;
	};

	void DisconnectLeft( void ) 
	{
		if( m_bLO_to_LI )
			m_pLeftOutputNerve->CutLeftConnectIn();
		else
			m_pLeftOutputNerve->CutRightConnectIn();
	};

	void DisconnectRight( void ) 
	{
		if( m_bRO_to_LI )
			m_pRightOutputNerve->CutLeftConnectIn();
		else
			m_pRightOutputNerve->CutRightConnectIn();
	};

	bool LeftConnectIn( CNerve* pNerve ) { if(m_pLeftInputNerve) return false; m_pLeftInputNerve=pNerve; return true;};
	bool RightConnectIn( CNerve* pNerve ) { if(m_pRightInputNerve) return false; m_pRightInputNerve=pNerve;return true;};

	bool ConnectIn( CNerve* pNerve ) { if(m_pMainInputNerve) return false; m_pMainInputNerve=pNerve;return true;};

	void CutLeftConnectIn( void ) {m_pLeftInputNerve=NULL;};
	void CutRightConnectIn( void ) {m_pRightInputNerve=NULL;};
	void CutConnectIn( void ) {m_pMainInputNerve=NULL;};
public:
	CNerve* m_pLeftInputNerve;
	CNerve* m_pRightInputNerve;
	CNerve* m_pLeftOutputNerve;
	CNerve* m_pRightOutputNerve;
	CNerve* m_pMainInputNerve;
	CNerve* m_pMainOutputNerve;
	bool m_bLO_to_LI; //true for left out connected to left in, otherwise connected to right in
	bool m_bRO_to_LI; //true for right out connected to left in, otherwise connected to right in
	bool m_bLeftSignalIsMinus;
	bool m_bRightSignalIsMinus;

protected:
	int m_nLeftInputSignal;
	int m_nRightInputSignal;
	int m_nLeftOutputSignal;
	int m_nRightOutputSignal;
	int m_nMainInputSignal;
	int m_nMainOutputSignal;

	int m_nLeftInputSignal_Buf;
	int m_nRightInputSignal_Buf;
	int m_nMainInputSignal_Buf;

	NERVE_ARROW_TYPE m_arrow_type;
	NERVE_POS_TYPE m_pos_type;
	int m_pos_x;
	int m_pos_y;

	int m_sig_buf[SIGNAL_BUF_SIZE];
	NERVE_STATUS m_status;
	int m_countdown;
};

class CNerveArray
{
public:
	CNerveArray(void):
	m_nWidth(0),
	m_nDeep(0),
	m_nInputGroupWidth(0),
	m_nOutputGroupWidth(0),
	m_pNerveArray(NULL),
	m_nRunSteps(0)
	{
	};
	virtual ~CNerveArray(void)
	{
		if(m_pNerveArray)
		{
			for( int i=0; i<m_nWidth*m_nDeep; i++ )
				delete m_pNerveArray[i];
			delete m_pNerveArray;
		}
	};

	void BuildArray( int nWidth, int nDeep, int nInputGroupWidth, int nOutputGroupWidth );

	void ConnectNerves( void );

	virtual CNerve* CreateNerve( NERVE_ARROW_TYPE a_tp, NERVE_POS_TYPE pos_tp, int nPos_x, int nPos_y )
	{return new CNerve( a_tp, pos_tp, nPos_x, nPos_y ); }; 

	void OneStepProcess( int* inputSignals, int* outputSignals ); 

	//void Inspire( void );
	//void Punish( void );

	CNerve** GetNerves( int& nWidth, int& nDeep ) {return m_pNerveArray;};

	CNerve* GetNerve( int pos_x, int pos_y ) { return m_pNerveArray[pos_x+pos_y*m_nDeep]; };

	int Save( unsigned char* pNerveArrayMem );
	bool Load( unsigned char* pNerveArrayMem );

protected:
	int m_nWidth;
	int m_nDeep;
	int m_nInputGroupWidth;
	int m_nOutputGroupWidth;
	int m_nRunSteps;
	CNerve** m_pNerveArray;
};

class CBrain
{
public:
	CBrain(void);
	virtual ~CBrain(void);

	//void Create( int nMsgCount, int nActionCount, SMsgPriority *MsgPriority );
	//void Think( CMsgArray *inputMsgs, CMsgArray *outputMsgs );

	void Inspire( void );
	void Punish( void );

	CBrain* Clone( void );
	CBrain* Breed( CBrain& wife );

	CNerveArray* GetNerveArray( void ) { return m_pNerveArray;};
private:
	int m_nMsgCount;
	int m_nActionCount;

	CNerveArray *m_pNerveArray;
};
